﻿using System;
using System.IO;
using System.Threading.Tasks;
using BookSleeve;
using RedisDemos.Extensions;

namespace RedisDemos.Scenarios
{
    public class UserRegistrationAndProfile : Scenario
    {
        protected override async Task Perform(RedisConnection conn)
        {
            await RegisterUser(conn, new UserModel
            {
                UserName = "tank-1",
                Password = "not safe pwd",
                Email = "some.fake@email.com",
                JoinedOn = DateTime.Today.AddDays(-1),
            });

            await RegisterUser(conn, new UserModel
            {
                UserName = "mario.rossi",
                Password = "another not safe pwd",
                Email = "mario.rossi@email.com",
                JoinedOn = DateTime.Today,
            });

            var profile = await FetchProfile(conn, "tank-1");
            "Profile before:".LogInfo();
            Print(profile);

            profile.FirstName = "Matteo";
            profile.LastName = "Baglini";
            profile.SubscribeNewsletter = true;
            profile.Twitter = "@DotNetToscana";
            await SaveProfile(conn, profile);

            profile = await FetchProfile(conn, "tank-1");
            "Profile after:".LogInfo();
            Print(profile);

            await UploadAvatar(conn, "tank-1", File.ReadAllBytes("logo.png"));
        }

        private async Task RegisterUser(RedisConnection conn, UserModel model)
        {
            // SADD key member [member ...] 
            var wasAdded = await conn.Sets.Add(Db, "username:all", model.UserName);
            if (wasAdded == false)
                throw new InvalidOperationException("User name already exists.");

            // INCR key 
            var newId = await conn.Strings.Increment(Db, "users:nextid");

            // SET key value
            // HMSET key field value [field value ...] 
            await Task.WhenAll(conn.Strings.Set(Db, String.Format("username:{0}", model.UserName), newId),
                               conn.Hashes.Set(Db, String.Format("user:{0}", newId), model.AsHash()));
        }

        private async Task<ProfileModel> FetchProfile(RedisConnection conn, String username)
        {
            if (String.IsNullOrWhiteSpace(username))
                throw new InvalidOperationException("The user name provided is invalid.");

            // GET key
            var userId = await conn.Strings.Get(Db, String.Format("username:{0}", username));
            if (userId == null)
                throw new InvalidOperationException("The user name provided is unknown.");

            // HGETALL key 
            var fields = await conn.Hashes.GetAll(Db, String.Format("user:{0}", userId.AsString()));
            return fields.AsObject<ProfileModel>();
        }

        private async Task SaveProfile(RedisConnection conn, ProfileModel profile)
        {
            // GET key
            var userId = await conn.Strings.Get(Db, String.Format("username:{0}", profile.UserName));
            if (userId == null)
                throw new InvalidOperationException("The user name provided is unknown.");

            // HMSET key field value [field value ...] 
            await conn.Hashes.Set(Db, String.Format("user:{0}", userId.AsString()), profile.AsHash());
        }

        private async Task UploadAvatar(RedisConnection conn, String userName, Byte[] avatar)
        {
            // GET key
            var userId = await conn.Strings.Get(Db, String.Format("username:{0}", userName));
            if (userId == null)
                throw new InvalidOperationException("The user name provided is unknown.");

            // HSET key field value 
            await conn.Hashes.Set(Db, String.Format("user:{0}", userId.AsString()), "avatar", avatar);
        }

        private static void Print(ProfileModel profile)
        {
            "\tFirstName: {0}".LogInfo(profile.FirstName);
            "\tLastName: {0}".LogInfo(profile.LastName);
            "\tSubscribeNewsletter: {0}".LogInfo(profile.SubscribeNewsletter);
            "\tTwitter: {0}".LogInfo(profile.Twitter);
        }
    }
}
